// 2675. 将对象数组转换为矩阵
// 提示
// 中等
// 2
// 编写一个函数，将对象数组 arr 转换为矩阵 m 。
//
// arr 是一个由对象组成的数组或一个数组。数组中的每个项都可以包含深层嵌套的子数组和子对象。它还可以包含数字、字符串、布尔值和空值。
//
// 矩阵 m 的第一行应该是列名。如果没有嵌套，列名是对象中的唯一键。如果存在嵌套，列名是对象中相应路径，以点号 "." 分隔。
//
// 剩余的每一行对应 arr 中的一个对象。矩阵中的每个值对应对象中的一个值。如果给定对象在给定列中没有值，则应该包含空字符串 "" 。
//
// 矩阵中的列应按 字典升序 排列。
//
//
//
// 示例 1：
//
// 输入：
// arr = [
//   {"b": 1, "a": 2},
//   {"b": 3, "a": 4}
// ]
// 输出：
// [
//   ["a", "b"],
//   [2, 1],
//   [4, 3]
// ]
//
// 解释：
// 两个对象中有两个唯一的列名："a"和"b"。
// "a"对应[2, 4]。
// "b"对应[1, 3]。
// 示例 2：
//
// 输入：
// arr = [
//   {"a": 1, "b": 2},
//   {"c": 3, "d": 4},
//   {}
// ]
// 输出：
// [
//   ["a", "b", "c", "d"],
//   [1, 2, "", ""],
//   ["", "", 3, 4],
//   ["", "", "", ""]
// ]
//
// 解释：
// 有四个唯一的列名："a"、"b"、"c"、"d"。
//  第一个对象具有与"a"和"b"关联的值。
// 第二个对象具有与"c"和"d"关联的值。
// 第三个对象没有键，因此只是一行空字符串。

function getPropPaths(o) {
  const res = []
  const helper = (o, path, res) => {
    if (typeof o !== 'object' || o === null) {
      res.push(path.join('.'))
      return
    }

    Object.keys(o).forEach(prop => {
      path.push(prop)
      helper(o[prop], path, res)
      path.pop()
    })
  }
  helper(o, [], res)

  return res
}

function traverse(o, cb) {
  const res = []
  const helper = (o, path, res) => {
    if (typeof o !== 'object' || o === null) {
      const propPath = path.join('.')
      cb(propPath, o)
      return
    }

    Object.keys(o).forEach(prop => {
      path.push(prop)
      helper(o[prop], path, res)
      path.pop()
    })
  }
  helper(o, [], res)

  return res
}

/**
 * @param {Array} arr
 * @return {Matrix}
 */
var jsonToMatrix = function (arr) {
  // prepare
  const keySet = arr.reduce((s, o) => (getPropPaths(o).forEach(path => s.add(path)), s), new Set())
  const keys = [...keySet].sort()
  const key2ColMap = new Map(keys.map((item, idx) => [item, idx]))
  const colN = keys.length

  // handle
  return arr.reduce((res, o, idx) => {
    const row = res[idx + 1] = new Array(colN).fill('')
    traverse(o, (propPath, val) =>
      row[key2ColMap.get(propPath)] = val
    )
    return res
  }, [keys.slice()])
}

console.log(
  jsonToMatrix(
    [
      [{"a": null}],
      [{"b": true}],
      [{"c": "x"}]
    ]
  )
)
